package com.zyl.Ecs.device.service.impl;

import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.github.yulichang.base.MPJBaseServiceImpl;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import com.zyl.Ecs.common.domain.device.bean.Position;
import com.zyl.Ecs.common.domain.device.bean.Room;
import com.zyl.Ecs.common.domain.device.bean.Sensor;
import com.zyl.Ecs.common.domain.device.ext.PositionExt;
import com.zyl.Ecs.common.domain.device.ext.SensorExt;
import com.zyl.Ecs.common.exception.EcsException;
import com.zyl.Ecs.common.response.ResultCode;
import com.zyl.Ecs.common.utils.ObjectUtil;
import com.zyl.Ecs.device.mapper.PositionMapper;
import com.zyl.Ecs.device.mapper.RoomMapper;
import com.zyl.Ecs.device.mapper.SensorMapper;
import com.zyl.Ecs.device.mapper.ext.PositionExtMapper;
import com.zyl.Ecs.device.service.PositionService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.stream.Collectors;


/**
* @version v1.0
* @Package: com.zyl.Ecs.device.service.impl
* @ClassName: PositionServiceImpl
* @Description 针对表【ecs_position】的数据库操作Service实现
* @Author 翟玉龙
* @Date 2024-12-05 09:52:35
*/

@Service
@RequiredArgsConstructor
public class PositionServiceImpl extends MPJBaseServiceImpl<PositionMapper, Position>
    implements PositionService{

    private final RoomMapper roomMapper;
    private final SensorMapper sensorMapper;
    private final PositionMapper positionMapper;
    private final PositionExtMapper positionExtMapper;

    /**
     * 新增点位
     *
     * @param positionExt 点位扩展信息
     * @return {@link PositionExt }
     * @throws EcsException 异常
     */
    @Override
    @Transactional
    public PositionExt add(PositionExt positionExt) throws EcsException {
        /* 检查点位名称是否重复 */
        String name = positionExt.getName();
        checkNameUnique(name, null);
        /* 检查车间ID是否存在 */
        checkRoomExist(positionExt.getRoomId());

        /* 插入点位信息 */
        /* 默认不删除 */
        positionExt.setDeleted(0);
        positionMapper.insert(positionExt);

        /* 设置点位传感器关系 */
        setSensors(positionExt.getId(), positionExt.getSensorIds());

        return get(positionExt.getId());
    }

    /**
     * 修改点位信息
     *
     * @param positionExt 点位扩展信息
     * @return {@link PositionExt }
     * @throws EcsException 异常
     */
    @Override
    public PositionExt update(PositionExt positionExt) throws EcsException {
        /* 检查ID是否存在 */
        Integer id = positionExt.getId();
        internalGet(id);
        /* 检查点位名称是否重复 */
        String name = positionExt.getName();
        checkNameUnique(name, id);
        /* 检查车间ID是否存在 */
        checkRoomExist(positionExt.getRoomId());

        /* 执行修改 */
        positionMapper.updateById(positionExt);

        /* 设置点位传感器关系 */
        setSensors(id, positionExt.getSensorIds());

        return get(id);
    }

    /**
     * 删除点位
     *
     * @param id 点位id
     * @return {@link PositionExt }
     * @throws EcsException 异常
     */
    @Override
    public PositionExt delete(Integer id) throws EcsException {
        /* 检查ID是否存在 */
        PositionExt positionExt = get(id);

        /*
         * 点位删除时，存在绑定传感器的处理办法
         * 1、点位删除，自动解除点位绑定传感器的关联，可以通过两种方式实现：
         * 1）数据库层面实现，在传感器表的position_id外键字段设置级联操作set null，被依赖字段值删除后自动将外键置空（需要有物理外键支持）
         * 2）程序逻辑实现，删除点位后程序自动将绑定的传感器点位ID置空，代码如下：
         */
        sensorMapper.update(null, Wrappers.<Sensor>lambdaUpdate()
                .eq(Sensor::getPositionId, id)
                .set(Sensor::getPositionId, null));

        /*
         * 2、设定规则为点位下包含绑定传感器时，禁止删除，代码如下
         */
        long sensorCount = sensorMapper.selectCount(Wrappers.<Sensor>lambdaQuery()
                .eq(Sensor::getPositionId, id));
        /* 直接比较结果 */
        if (sensorCount != 0) {
            throw new EcsException(ResultCode.POSITION_DELETE_FORBIDDEN);
        }

        /* 执行删除 */
        positionMapper.deleteById(id);

        return positionExt;
    }

    /**
     * 查询全部点位列表
     *
     * @return {@link List }<{@link PositionExt }>
     * @throws EcsException 异常
     */
    @Override
    public List<PositionExt> getList() throws EcsException {
        MPJLambdaWrapper<PositionExt> query = new MPJLambdaWrapper<>();
        query
                /* 查询所有的点位Ext内容 */
                .selectAll(PositionExt.class)
                /* 查询所有的Room内容并且全部映射到点位Ext中 (一对一）*/
                .selectAssociation(Room.class, PositionExt::getRoom)
                /* 查询所有的传感器ID并且全部映射到点位Ext中的id列表 (一对多）*/
                /* map指定获取字段 */
                .selectCollection(Sensor.class, PositionExt::getSensorIds, map -> map
                        .result(Sensor::getId)
                )
                .selectCollection(Sensor.class, PositionExt::getSensors)
                .leftJoin(Room.class, Room::getId, PositionExt::getRoomId)
                .leftJoin(Sensor.class, Sensor::getPositionId, PositionExt::getId);
        return positionExtMapper.selectList(query);
    }

    /**
     * 查询点位信息
     * @param id integer
     * @param sensorIds 传感器id列表
     */
    private void setSensors(Integer id, List<Integer> sensorIds) {

        /* 如果传入的传感器ID列表为空，则表示取消该点位下的所有传感器关联（将该点位下所有传感器的点位ID置空） */
        if (ObjectUtil.isEmpty(sensorIds)) {
            sensorMapper.update(null, Wrappers.<Sensor>lambdaUpdate()
                    .eq(Sensor::getPositionId, id)
                    .set(Sensor::getPositionId, null));
            return;
        }

        /*
         * 检查传感器状态是否合法，如果存在传感器positionId不为空且不是当前position的ID，则状态错误
         */
        for (Integer sensorId : sensorIds) {
            Sensor sensor = sensorMapper.selectById(sensorId);
            if (ObjectUtil.isNull(sensor)) {
                throw new EcsException(ResultCode.SENSOR_NOT_EXIST);
            }
            if (ObjectUtil.and(
                    ObjectUtil.nonNull(sensor.getPositionId()),
                    ObjectUtil.equals(sensor.getPositionId(), id))) {
                throw new EcsException(ResultCode.SENSOR_BOUND_FORBIDDEN);
            }
            sensor.setPositionId(id);
            sensorMapper.updateById(sensor);
        }
    }

    /**
     * 为点位查询传感器列表
     *
     * @param positionExt 点位扩展信息
     * @return {@link PositionExt }
     */
    private PositionExt fillSensors(PositionExt positionExt) {
        positionExt.setSensors(sensorMapper.selectList(Wrappers.<Sensor>lambdaQuery()
                        .eq(Sensor::getPositionId, positionExt.getId()))
                .stream()
                .map(SensorExt::toExt)
                .collect(Collectors.toList()));
        return positionExt;
    }

    /**
     * 根据ID查询点位信息（包含传感器）
     *
     * @param id 点位Id
     * @return {@link PositionExt }
     */
    private PositionExt get(Integer id) {
        return fillSensors(PositionExt.toExt(internalGet(id)));
    }

    /**
     * 根据ID查询点位信息
     *
     * @param id 点位Id
     * @return {@link Position }
     * @throws EcsException 异常
     */
    private Position internalGet(Integer id) throws EcsException {
        Position position = positionMapper.selectById(id);
        if (ObjectUtil.isNull(position)) {
            throw new EcsException(ResultCode.POSITION_NOT_EXIST);
        }
        return position;
    }

    /**
     * 检查房间是否存在
     *
     * @param roomId 房间ID
     */
    private void checkRoomExist(Integer roomId) {
        Room room = roomMapper.selectById(roomId);
        if (ObjectUtil.isNull(room)) {
            throw new EcsException(ResultCode.ROOM_NOT_EXIST);
        }
    }

    /**
     * 检查点位名称是否唯一
     *
     * @param name 点位名称
     * @param id   点位ID
     */
    private void checkNameUnique(String name, Integer id) {
        Position record = positionMapper.selectOne(Wrappers.<Position>lambdaQuery()
                .eq(Position::getName, name));
        if (ObjectUtil.nonNull(record) && ObjectUtil.notEquals(record.getId(), id)) {
            throw new EcsException(ResultCode.POSITION_NAME_DUPLICATE);
        }
    }
}




